﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Unity.Collections.LowLevel.Unsafe;

using Unity.Collections;
using System.Diagnostics;




#if UNITY_EDITOR
using UnityEngine;
using UnityEditor;
#endif

namespace USArray
{

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct ArrayHeader
    {

        public IntPtr objid;    //objheader + [padding] so IntPtr always good.
        public IntPtr type;     //typehander is IntPtr
#if UNITY_2019_1_OR_NEWER
        public IntPtr type2;    //多了8字节，与文档很不同
#else

#endif
        public IntPtr length;   //length +[padding] so IntPtr is good.
        public override string ToString()
        {
#if UNITY_2019_1_OR_NEWER
            return " obj :" + objid.ToString("X16") + " type:" + type.ToString("X16") + "," + type2.ToString("X16") + " len=" + length.ToString("X16");
#else
            return " obj :" + objid.ToString("X16") + " type:" + type.ToString("X16") + " len=" + length.ToString("X16");
#endif
        }
    }
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct StringHeader
    {
        public IntPtr objid; //objheader + [padding] so IntPtr always good.
        public IntPtr type;  //typehander is IntPtr
        public int length;
        public override string ToString()
        {
            return "obj :" + objid.ToString("X16") + " type:" + type.ToString("X16") + " len=" + length.ToString("X08");
        }
    }
#if UNITY_2019_1_OR_NEWER
    [StructLayout(LayoutKind.Explicit)]
    public class PtrBox
    {

        [FieldOffset(0)]
        public object obj;
        [FieldOffset(0)]
        public IntPtr ptr;
        [FieldOffset(0)]
        public unsafe void* unsafeptr;
    }
#else
    [StructLayout(LayoutKind.Explicit)]
    public class PtrBox
    {

        public unsafe delegate object deleGetObjSrc(object obj);
        public unsafe delegate object deleGetObj(void* obj);
        public unsafe delegate object deleGetObjH(IntPtr obj);
        public unsafe delegate void* deleGetPtr(object obj);
        public unsafe delegate IntPtr deleGetPtrH(object obj);

        [FieldOffset(0)]
        public unsafe deleGetObjSrc GetObjSrc;
        [FieldOffset(0)]
        public unsafe deleGetPtr GetPtr;
        [FieldOffset(0)]
        public unsafe deleGetPtrH GetPtrH;
        [FieldOffset(0)]
        public unsafe deleGetObj GetObj;
        [FieldOffset(0)]
        public unsafe deleGetObjH GetObjH;

        //    [FieldOffset(0)]
        //    public IntPtr ptr;
        //    [FieldOffset(0)]
        //    public unsafe void* unsafeptr;

    }
#endif

    public class UnsafePtrTool
    {
        static UnsafePtrTool()
        {

        }
        static PtrBox ptrbox = new PtrBox();
#if UNITY_2019_1_OR_NEWER
        public static T FromPtr<T>(IntPtr ptr) where T : class
        {
            unsafe
            {
                ptrbox.ptr = ptr;
                return ptrbox.obj as T;
            }
        }
        public static IntPtr ToPtr<T>(T obj) where T : class
        {
            unsafe
            {
                ptrbox.obj = obj;
                return ptrbox.ptr;
            }

        }
        public static unsafe T FromPtrUnsafe<T>(void* ptr) where T : class
        {


            ptrbox.unsafeptr = ptr;
            return ptrbox.obj as T;
        }
        public static unsafe void* ToPtrUnsafe<T>(T obj) where T : class
        {
            ptrbox.obj = obj;
            return ptrbox.unsafeptr;
        }
#else
        public static T FromPtr<T>(IntPtr ptr) where T : class
        {
            return ptrbox.GetObjH(ptr + IntPtr.Size) as T;
        }
        public static IntPtr ToPtr<T>(T obj) where T : class
        {

            return ptrbox.GetPtrH(obj) - IntPtr.Size;

        }
        public static unsafe T FromPtrUnsafe<T>(void* ptr) where T : class
        {
            return ptrbox.GetObj((byte*)ptr + IntPtr.Size) as T;

        }
        public static unsafe void* ToPtrUnsafe<T>(T obj) where T : class
        {
            return (byte*)ptrbox.GetPtr(obj) - IntPtr.Size;
        }
#endif

        public static unsafe StringHeader* GetStringHeader(string str)
        {
            unsafe
            {
                fixed (void* pstr = str)
                {
                    return ((StringHeader*)pstr - 1);
                }
            }
        }


        public static unsafe ArrayHeader* GetArrHeader<T>(T[] arr) where T : unmanaged
        {
            unsafe
            {
                fixed (T* pstr = arr)
                {
                    return ((ArrayHeader*)pstr - 1);
                }
            }
        }
#if UNITY_2019_1_OR_NEWER

#if UNITY_EDITOR

        class TrackMem
        {
            public unsafe void* ptr;
            public System.Diagnostics.StackTrace stacktrace;
        }
        static Dictionary<IntPtr, TrackMem> persisitptrs = new Dictionary<IntPtr, TrackMem>();

        [InitializeOnLoadMethod]
        public static void SafeFunc()
        {
            EditorApplication.playModeStateChanged += (PlayModeStateChange c) =>
            {
                if (c == PlayModeStateChange.ExitingPlayMode)
                {
                    if (persisitptrs.Count == 0)
                        return;

                    UnityEngine.Debug.LogWarning("==>FreeOn ExitingPlayMode<== count=" + persisitptrs.Count);
                    var assetpath = Application.dataPath;
                    UnityEngine.Debug.LogWarning("==>assetpath:" + assetpath);
                    foreach (var p in persisitptrs)
                    {
                        unsafe
                        {
                            void* ptr = (void*)p.Key;
                            StringBuilder sb = new StringBuilder();
                            var fs = p.Value.stacktrace.GetFrames();
                            foreach (var f in fs)
                            {
                                var m = f.GetMethod();
                                var filename = f.GetFileName();
                                for (var i = 0; i < 10; i++)
                                {
                                    filename = filename.Replace('\\', '/');

                                }
                                if (filename.IndexOf(assetpath) == 0)
                                {
                                    filename = filename.Substring(assetpath.Length - 6);
                                }
                                sb.AppendLine(m.DeclaringType.Name + "::" + m.Name + " (at " + filename + ":" + f.GetFileLineNumber() + ")"); ;
                            }
                         
                            UnityEngine.Debug.LogError("==>NoGC leak:<== \n" + sb.ToString() + "-----------end-------------");

                            Unity.Collections.LowLevel.Unsafe.UnsafeUtility.Free(ptr, Unity.Collections.Allocator.Persistent);
                            //FreeNoTemp(ptr);
                        }
                    }
                    persisitptrs.Clear();
                }
            };
        }
#endif

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void* Malloc(int size, bool temp)
        {
            if (temp)
            {
                return Unity.Collections.LowLevel.Unsafe.UnsafeUtility.Malloc(size, 1, Unity.Collections.Allocator.Temp);
            }
            else
            {
                var ptr = Unity.Collections.LowLevel.Unsafe.UnsafeUtility.Malloc(size, 1, Unity.Collections.Allocator.Persistent);


#if UNITY_EDITOR
                TrackMem track = new TrackMem();
                track.stacktrace = new StackTrace(1, fNeedFileInfo: true);

                persisitptrs.Add((IntPtr)ptr, track);
#endif
                return ptr;
            }
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void FreeNoTemp(void* ptr)
        {
#if UNITY_EDITOR
            if (persisitptrs.TryGetValue((IntPtr)ptr, out var p))
            {
                persisitptrs.Remove((IntPtr)ptr);
            }

#endif
            Unity.Collections.LowLevel.Unsafe.UnsafeUtility.Free(ptr, Unity.Collections.Allocator.Persistent);
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static unsafe void MemCpy(void* dest, void* src, int length)
        {
            Unity.Collections.LowLevel.Unsafe.UnsafeUtility.MemCpy(dest, src, length);
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void ResetTempAlloc()
        {

        }


#else

        public static unsafe void* Malloc(int size, bool temp)
        {
            if (temp)
            {
                if (tlsSize.Count <= tlsIndex)
                {
                    tlsSize.Add(0);
                    tlsMem.Add(new byte[16 * 1024 * 1024]);
                }

                var index = tlsSize[tlsIndex];
                var len = tlsMem[tlsIndex].Length;
                if (index + size > len)
                {
                    index = 0;
                    tlsIndex++;
                    if (tlsSize.Count <= tlsIndex)
                    {
                        tlsSize.Add(0);
                        tlsMem.Add(new byte[16 * 1024 * 1024]);
                    }

                }

                unsafe
                {
                    fixed (byte* ptr = tlsMem[tlsIndex])
                    {
                        tlsSize[tlsIndex] = tlsSize[tlsIndex] + size;

                        var nptr = (ptr + index);
                        return nptr;
                    }

                }

            }
            else
            {
                return (void*)Marshal.AllocHGlobal(size);
            }
        }
        public static unsafe void FreeNoTemp(void* ptr)
        {
            Marshal.FreeHGlobal((IntPtr)ptr);
        }
        public static unsafe void MemCpy(void* dest, void* src, int length)
        {
            Buffer.MemoryCopy(src, dest, length, length);
        }


        static List<byte[]> tlsMem = new List<byte[]>();
        static List<int> tlsSize = new List<int>();
        static int tlsIndex = 0;
        public static void ResetTempAlloc()
        {
            for (var i = 0; i < tlsSize.Count; i++)
            {
                tlsSize[i] = 0;
            }
            tlsIndex = 0;

        }
#endif
    }



}
